package com.watt.gateway.interceptor;

import com.alibaba.fastjson.JSON;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.watt.gateway.config.InterceptorConstant;
import com.watt.gateway.domain.FailResultEntity;
import lombok.extern.slf4j.Slf4j;
import com.watt.gateway.constant.Constant;
import com.watt.gateway.utils.RedisUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.zuul.filters.Route;
import org.springframework.cloud.netflix.zuul.filters.discovery.DiscoveryClientRouteLocator;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.Map;

@Component
@Slf4j
public class CustomZuulFilter extends ZuulFilter {
    private static Logger logger = LoggerFactory.getLogger(CustomZuulFilter.class);

    @Autowired
    RedisUtil cacheService;

    @Autowired
    InterceptorConstant interceptorConstant;

    /**
     * 过滤器类型
     * pre 事前
     * routing 路由请求时候调用
     * error 发生错误时候调用
     *
     * @return
     */
    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 1;
    }

    /**
     * 是否过滤
     * 0 不过滤
     * 1 过滤
     *
     * @return
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Autowired(required = false)
    private DiscoveryClientRouteLocator clientRouteLocator;

    /**
     * 拦截的具体操作
     * 验证token
     *
     * @return
     */
    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest req = ctx.getRequest();
        String token = req.getHeader(Constant.CURRENT_USER_TOKEN);
        //权限校验

        // 设置登录类型
        String terminal = req.getHeader(Constant.CURRENT_USER_TERMINAL);
        if (StringUtils.isNotEmpty(token) && StringUtils.isNotEmpty(terminal)) {
            cacheService.setKey(token + Constant.CURRENT_USER_TERMINAL, terminal);
        }
        //获取转发规则
        Route matchingRoute = clientRouteLocator.getMatchingRoute(req.getRequestURI());
        //获取服务Id
        String serviceId = matchingRoute.getLocation();
        //判断服务是否不需要token
        boolean isNotFilterServiceId = interceptorConstant.isNotInterceptorService(serviceId);
        if (isNotFilterServiceId){
            // 对该请求进行路由
            ctx.setSendZuulResponse(true);
            ctx.setResponseStatusCode(200);
            return null;
        }
        //  是否登录白名单,获取后缀地址
        String pathinfo = req.getRequestURI();
        Boolean isLogin = interceptorConstant.isPathInterceptor(pathinfo);
        // 是否根目录
        Boolean isRootUrl = isRootUrl(pathinfo);

        if (!isRootUrl && !isLogin) {
            if (StringUtils.isBlank(token)) {
                //不进行路由
                ctx.setSendZuulResponse(false);
                ctx.setResponseStatusCode(401);
                FailResultEntity reslut = new FailResultEntity(1020,"token is null");
                ctx.setResponseBody(JSON.toJSONString(reslut));
                return null;
            }

            Object userInfoVo = cacheService.getValue(token);
            if (userInfoVo == null) {
                //不进行路由
                ctx.setSendZuulResponse(false);
                ctx.setResponseStatusCode(401);
                FailResultEntity reslut = new FailResultEntity(1020,"token expired");
                ctx.setResponseBody(JSON.toJSONString(reslut));
                return null;
            }

            // object 转 map
            Map userMap = transBean2Map(userInfoVo);
            // 用户ID放入request
            req.setAttribute(Constant.CURRENT_USER_ID, userMap.get("userId"));
            // 对该请求进行路由
            ctx.setSendZuulResponse(true);
            ctx.setResponseStatusCode(200);
            // 设值，可以在多个过滤器时使用
            ctx.set(Constant.CURRENT_USER_ID, userMap.get("userId"));
            return null;
        }
        // 对该请求进行路由
        ctx.setSendZuulResponse(true);
        ctx.setResponseStatusCode(200);
        logger.info("token验证成功");
        return null;
    }

    private Boolean isRootUrl(String path) {
        if (path.equals("/")) {
            return true;
        }
        return false;
    }

    /**
     * 获取客户端IP地址
     *
     * @param request
     * @return
     */
    private String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
            if (ip.equals("127.0.0.1")) {
                //根据网卡取本机配置的IP
                InetAddress inet = null;
                try {
                    inet = InetAddress.getLocalHost();
                    ip = inet.getHostAddress();
                } catch (Exception e) {
                    log.error("CustomZuulFilter.getIpAddress",e);
                }
            }
        }
        // 多个代理的情况，第一个IP为客户端真实IP,多个IP按照','分割
        if (ip != null && ip.length() > 15) {
            if (ip.indexOf(",") > 0) {
                ip = ip.substring(0, ip.indexOf(","));
            }
        }
        return ip;
    }


    /**
     * 私有方法 将对象专成map
     */
    private Map<String, Object> transBean2Map(Object obj) {
        // Bean --> Map 1: 利用Introspector和PropertyDescriptor 将Bean --> Map
        if (obj == null) {
            return null;
        }
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor property : propertyDescriptors) {
                String key = property.getName();
                // 过滤class属性
                if (!key.equals("class")) {
                    // 得到property对应的getter方法
                    Method getter = property.getReadMethod();
                    Object value = getter.invoke(obj);
                    map.put(key, value);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }
}
